Skip to content

feat(accounts): serve harness credentials from the vault, and mint keys as a principal - #27

Merged
pyramation merged 1 commit into
mainfrom
feat/harness-credentials-principals
Aug 9, 2026
Merged

feat(accounts): serve harness credentials from the vault, and mint keys as a principal#27
pyramation merged 1 commit into
mainfrom
feat/harness-credentials-principals

Conversation

@pyramation

Copy link
Copy Markdown
Contributor

Summary

Two things that make dcrypt useful to something other than a human at the UI.

1. dcrypt as a credential provider. @agentic-kit/harness doesn't do auth — it defines the contract and makes the host supply it. VaultCredentials is that host, reading the unlocked vault instead of a plaintext account.json:

class VaultCredentials {
  accountBearer(): Promise<string | null>          // the signed-in account's token
  dataToken(databaseId): Promise<DataTokenResult>  // { token, origin: 'vault' }
}

The interesting part is what it won't do. It refuses rather than guesses: null when no account is signed in, when several are and none was named, when the token has expired, and when zero — or two — keys claim the database. Nothing is cached, so every call re-reads the vault and locking cuts every consumer off at once. A data token is served even while its account is signed out, because an API key is its own credential, not a derivative of a session.

Keys learn which database they are for, as ordinary (non-concealed) metadata:

createApiKey(accountItemId, { name: 'ci', databaseId })
assignKeyToDatabase(keyItemId, databaseId)  // touches only the tag, never the secret

2. Principals. A Constructive principal is a scoped sub-identity — what a key or an agent actually acts as — owned by a human and able only to narrow them: isReadOnly, a per-scope allowedMask AND-ed with the owner's permissions during the SPRT cascade, and bypassStepUp so CI isn't blocked on someone's phone. No override row for a scope means it simply inherits the owner there, which the CLI says out loud rather than rendering an empty list.

const principalId = await accounts.createPrincipal(itemId, { name: 'ci-deploy', orgId, isReadOnly: true });
await accounts.createApiKey(itemId, { name: 'ci', principalId, orgId });  // → createOrgApiKey

So "ci-deploy-key" becomes an identity with its own mask instead of a copy of your access. createApiKey routes to createOrgApiKey when an orgId is given; both carry principalId. Principals are read from the server on demand and deliberately not mirrored into the vault — a stale local copy of someone's permissions is worse than none. Creating and deleting one goes through the same withStepUp hold-and-replay as every other sensitive call.

CLI:

dcrypt account token [email] [--database <id>]
dcrypt account key create ci --principal <id> --org <id> --database <id>
dcrypt account key assign ci db-1
dcrypt account principal list|create|delete

Tests

packages/accounts covers the refusals specifically — ambiguous accounts, ambiguous databases, expired tokens, a data token surviving sign-out, and that assignKeyToDatabase leaves the concealed secret untouched — plus principal creation, step-up on creation, deletion, and refusing to reach the server when signed out. Repo lint, build, test and desktop typecheck are green.

Not in this PR: the desktop UI for principals, and the IPC that would expose the provider to another process.

Link to Devin session: https://app.devin.ai/sessions/04636534e07048089ffb6b78142e12cd
Requested by: @pyramation

…ys as a principal

The harness asks a host for an account bearer and a per-database data-plane
token; VaultCredentials answers both out of the unlocked vault, so consumers
stop keeping their own plaintext account.json. It refuses rather than guesses
when the answer is ambiguous.

Keys can also be minted as a Constructive principal - a scoped sub-identity
that can only narrow its owner - so a CI key carries its own permission mask
instead of a copy of a human's access.
@pyramation pyramation self-assigned this Aug 9, 2026
@devin-ai-integration

Copy link
Copy Markdown

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

@pyramation
pyramation merged commit 8dccfc3 into main Aug 9, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant